use crate::error::{CompilerError, Result};
use crate::llvm_backend::LLVMBackend;
use inkwell::{values::BasicValueEnum, AddressSpace, IntPredicate};

impl<'ctx> LLVMBackend<'ctx> {

    /// Generate string.trim() method call
    /// Removes leading and trailing whitespace characters
    ///
    /// Example: "  hello world  ".trim() returns "hello world"
    /// This is implemented by combining trim_start() and trim_end()
    pub(in crate::llvm_backend) fn generate_string_trim_method(
        &mut self,
        var_name: &str,
    ) -> Result<BasicValueEnum<'ctx>> {
        // First apply trim_start()
        let trimmed_start = self.generate_string_trim_start_method(var_name)?;

        // Create a temporary variable to store the result of trim_start()
        let temp_var_name = format!("{}_trimmed_start", var_name);
        let temp_alloca = self.builder.build_alloca(
            self.context.i8_type().ptr_type(AddressSpace::default()),
            &temp_var_name,
        )?;
        self.builder
            .build_store(temp_alloca, trimmed_start.into_pointer_value())?;

        // Store the temporary variable in variable_map for the next call
        self.variable_map
            .insert(temp_var_name.clone(), temp_alloca.into());

        // Then apply trim_end() to the result
        let final_result = self.generate_string_trim_end_method(&temp_var_name)?;

        // Clean up the temporary variable
        self.variable_map.remove(&temp_var_name);

        Ok(final_result)
    }


    /// Generate string.trim_start() method call
    /// Removes leading whitespace characters
    ///
    /// Example: "  hello world  ".trim_start() returns "hello world  "
    pub(in crate::llvm_backend) fn generate_string_trim_start_method(
        &mut self,
        var_name: &str,
    ) -> Result<BasicValueEnum<'ctx>> {
        // Get the string pointer
        let string_ptr = if let Some(var_value) = self.variable_map.get(var_name) {
            var_value.into_pointer_value()
        } else {
            return Err(CompilerError::internal(&format!(
                "Undefined variable: {}",
                var_name
            )));
        };

        // Load the string pointer value (i8*)
        let i8_ptr_type = self.context.i8_type().ptr_type(AddressSpace::default());
        let string_value = self
            .builder
            .build_load(i8_ptr_type, string_ptr, "string_val")?;
        let original_str_ptr = string_value.into_pointer_value();

        let i8_type = self.context.i8_type();
        let i32_type = self.context.i32_type();
        let i64_type = self.context.i64_type();

        // Get strlen function
        let strlen_fn_type = i32_type.fn_type(&[i8_ptr_type.into()], false);
        let strlen_fn = if let Some(func) = self.module.get_function("strlen") {
            func
        } else {
            self.module.add_function("strlen", strlen_fn_type, None)
        };

        // Get string length
        let str_len_i32 = self
            .builder
            .build_call(strlen_fn, &[original_str_ptr.into()], "str_len_i32")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_int_value();
        let str_len = self
            .builder
            .build_int_z_extend(str_len_i32, i64_type, "str_len")?;

        // Create basic blocks
        let current_func = self.current_function.unwrap();
        let empty_check_block = self
            .context
            .append_basic_block(current_func, "trim_start_empty_check");
        let find_start_block = self
            .context
            .append_basic_block(current_func, "trim_start_find_start");
        let alloc_block = self
            .context
            .append_basic_block(current_func, "trim_start_alloc");
        let copy_block = self
            .context
            .append_basic_block(current_func, "trim_start_copy");
        let return_block = self
            .context
            .append_basic_block(current_func, "trim_start_return");

        // Jump to empty check
        self.builder.build_unconditional_branch(empty_check_block)?;

        // Empty check: if len == 0, return empty string
        self.builder.position_at_end(empty_check_block);
        let is_empty = self.builder.build_int_compare(
            IntPredicate::EQ,
            str_len,
            i64_type.const_int(0, false),
            "is_empty",
        )?;
        self.builder
            .build_conditional_branch(is_empty, return_block, find_start_block)?;

        // Find start index (first non-whitespace character)
        self.builder.position_at_end(find_start_block);
        let start_idx_alloca = self.builder.build_alloca(i64_type, "start_idx")?;
        self.builder
            .build_store(start_idx_alloca, i64_type.const_int(0, false))?;

        let start_loop = self
            .context
            .append_basic_block(current_func, "trim_start_start_loop");
        let start_loop_body = self
            .context
            .append_basic_block(current_func, "trim_start_start_loop_body");
        let start_loop_end = self
            .context
            .append_basic_block(current_func, "trim_start_start_loop_end");

        self.builder.build_unconditional_branch(start_loop)?;

        // Start loop: find first non-whitespace character
        self.builder.position_at_end(start_loop);
        let start_idx = self
            .builder
            .build_load(i64_type, start_idx_alloca, "start_idx")?
            .into_int_value();

        // Check if start_idx < str_len
        let continue_start = self.builder.build_int_compare(
            IntPredicate::SLT,
            start_idx,
            str_len,
            "continue_start",
        )?;
        self.builder
            .build_conditional_branch(continue_start, start_loop_body, start_loop_end)?;

        // Start loop body: check if character is whitespace
        self.builder.position_at_end(start_loop_body);
        let start_char_ptr = unsafe {
            self.builder
                .build_gep(i8_type, original_str_ptr, &[start_idx], "start_char_ptr")?
        };
        let start_char = self
            .builder
            .build_load(i8_type, start_char_ptr, "start_char")?
            .into_int_value();

        // Check if character is whitespace (space, tab, newline, carriage return)
        // ASCII: space=32, tab=9, newline=10, carriage return=13
        let is_whitespace = self.builder.build_or(
            self.builder.build_or(
                self.builder.build_or(
                    self.builder.build_int_compare(
                        IntPredicate::EQ,
                        start_char,
                        i8_type.const_int(32, false), // space
                        "is_space",
                    )?,
                    self.builder.build_int_compare(
                        IntPredicate::EQ,
                        start_char,
                        i8_type.const_int(9, false), // tab
                        "is_tab",
                    )?,
                    "is_space_or_tab",
                )?,
                self.builder.build_int_compare(
                    IntPredicate::EQ,
                    start_char,
                    i8_type.const_int(10, false), // newline
                    "is_newline",
                )?,
                "is_space_tab_or_newline",
            )?,
            self.builder.build_int_compare(
                IntPredicate::EQ,
                start_char,
                i8_type.const_int(13, false), // carriage return
                "is_cr",
            )?,
            "is_whitespace",
        )?;

        // If not whitespace, we found the start; otherwise increment
        let start_found_block = self
            .context
            .append_basic_block(current_func, "trim_start_start_found");
        let start_increment_block = self
            .context
            .append_basic_block(current_func, "trim_start_start_increment");

        self.builder.build_conditional_branch(
            is_whitespace,
            start_increment_block,
            start_found_block,
        )?;

        // Increment start index
        self.builder.position_at_end(start_increment_block);
        let next_start_idx = self.builder.build_int_add(
            start_idx,
            i64_type.const_int(1, false),
            "next_start_idx",
        )?;
        self.builder.build_store(start_idx_alloca, next_start_idx)?;
        self.builder.build_unconditional_branch(start_loop)?;

        // Start found: calculate length and allocate
        self.builder.position_at_end(start_found_block);
        let final_start_idx = self
            .builder
            .build_load(i64_type, start_idx_alloca, "final_start_idx")?
            .into_int_value();
        let trimmed_len = self
            .builder
            .build_int_sub(str_len, final_start_idx, "trimmed_len")?;
        let trimmed_len_plus_one = self.builder.build_int_add(
            trimmed_len,
            i64_type.const_int(1, false),
            "trimmed_len_plus_one",
        )?;

        // Check if trimmed length is 0
        let is_trimmed_empty = self.builder.build_int_compare(
            IntPredicate::EQ,
            trimmed_len,
            i64_type.const_int(0, false),
            "is_trimmed_empty",
        )?;
        self.builder
            .build_conditional_branch(is_trimmed_empty, return_block, alloc_block)?;

        // Allocate memory for trimmed string
        self.builder.position_at_end(alloc_block);
        let malloc_fn_type = i8_ptr_type.fn_type(&[i64_type.into()], false);
        let malloc_fn = if let Some(func) = self.module.get_function("malloc") {
            func
        } else {
            self.module.add_function("malloc", malloc_fn_type, None)
        };

        let result_str = self
            .builder
            .build_call(malloc_fn, &[trimmed_len_plus_one.into()], "result_str")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_pointer_value();

        self.builder.build_unconditional_branch(copy_block)?;

        // Copy the trimmed substring
        self.builder.position_at_end(copy_block);
        let copy_idx_alloca = self.builder.build_alloca(i64_type, "copy_idx")?;
        self.builder
            .build_store(copy_idx_alloca, i64_type.const_int(0, false))?;

        let copy_loop = self
            .context
            .append_basic_block(current_func, "trim_start_copy_loop");
        let copy_loop_body = self
            .context
            .append_basic_block(current_func, "trim_start_copy_loop_body");
        let copy_loop_end = self
            .context
            .append_basic_block(current_func, "trim_start_copy_loop_end");

        self.builder.build_unconditional_branch(copy_loop)?;

        // Copy loop
        self.builder.position_at_end(copy_loop);
        let copy_idx = self
            .builder
            .build_load(i64_type, copy_idx_alloca, "copy_idx")?
            .into_int_value();

        // Check if copy_idx < trimmed_len
        let continue_copy = self.builder.build_int_compare(
            IntPredicate::SLT,
            copy_idx,
            trimmed_len,
            "continue_copy",
        )?;
        self.builder
            .build_conditional_branch(continue_copy, copy_loop_body, copy_loop_end)?;

        // Copy loop body
        self.builder.position_at_end(copy_loop_body);
        let src_idx = self
            .builder
            .build_int_add(final_start_idx, copy_idx, "src_idx")?;
        let src_char_ptr = unsafe {
            self.builder
                .build_gep(i8_type, original_str_ptr, &[src_idx], "src_char_ptr")?
        };
        let src_char = self
            .builder
            .build_load(i8_type, src_char_ptr, "src_char")?
            .into_int_value();

        let dst_char_ptr = unsafe {
            self.builder
                .build_gep(i8_type, result_str, &[copy_idx], "dst_char_ptr")?
        };
        self.builder.build_store(dst_char_ptr, src_char)?;

        // Increment copy index
        let next_copy_idx =
            self.builder
                .build_int_add(copy_idx, i64_type.const_int(1, false), "next_copy_idx")?;
        self.builder.build_store(copy_idx_alloca, next_copy_idx)?;
        self.builder.build_unconditional_branch(copy_loop)?;

        // Copy loop end: add null terminator
        self.builder.position_at_end(copy_loop_end);
        let null_ptr = unsafe {
            self.builder
                .build_gep(i8_type, result_str, &[trimmed_len], "null_ptr")?
        };
        self.builder
            .build_store(null_ptr, i8_type.const_int(0, false))?;
        self.builder.build_unconditional_branch(return_block)?;

        // Start loop end: all characters are whitespace
        self.builder.position_at_end(start_loop_end);
        self.builder.build_unconditional_branch(return_block)?;

        // Return block with PHI node
        self.builder.position_at_end(return_block);
        let result_phi = self.builder.build_phi(i8_ptr_type, "trim_start_result")?;

        // Empty string case: return ""
        let empty_str = self.builder.build_global_string_ptr("", "empty_str")?;
        result_phi.add_incoming(&[(&empty_str, empty_check_block)]);
        result_phi.add_incoming(&[(&empty_str, start_loop_end)]);
        result_phi.add_incoming(&[(&empty_str, start_found_block)]);

        // Normal case: return trimmed string
        result_phi.add_incoming(&[(&result_str, copy_loop_end)]);

        Ok(result_phi.as_basic_value())
    }


    /// Generate string.trim_end() method call
    /// Removes trailing whitespace characters
    ///
    /// Example: "  hello world  ".trim_end() returns "  hello world"
    pub(in crate::llvm_backend) fn generate_string_trim_end_method(
        &mut self,
        var_name: &str,
    ) -> Result<BasicValueEnum<'ctx>> {
        // Get the string pointer
        let string_ptr = if let Some(var_value) = self.variable_map.get(var_name) {
            var_value.into_pointer_value()
        } else {
            return Err(CompilerError::internal(&format!(
                "Undefined variable: {}",
                var_name
            )));
        };

        // Load the string pointer value (i8*)
        let i8_ptr_type = self.context.i8_type().ptr_type(AddressSpace::default());
        let string_value = self
            .builder
            .build_load(i8_ptr_type, string_ptr, "string_val")?;
        let original_str_ptr = string_value.into_pointer_value();

        let i8_type = self.context.i8_type();
        let i32_type = self.context.i32_type();
        let i64_type = self.context.i64_type();

        // Get strlen function
        let strlen_fn_type = i32_type.fn_type(&[i8_ptr_type.into()], false);
        let strlen_fn = if let Some(func) = self.module.get_function("strlen") {
            func
        } else {
            self.module.add_function("strlen", strlen_fn_type, None)
        };

        // Get string length
        let str_len_i32 = self
            .builder
            .build_call(strlen_fn, &[original_str_ptr.into()], "str_len_i32")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_int_value();
        let str_len = self
            .builder
            .build_int_z_extend(str_len_i32, i64_type, "str_len")?;

        // Create basic blocks
        let current_func = self.current_function.unwrap();
        let empty_check_block = self
            .context
            .append_basic_block(current_func, "trim_end_empty_check");
        let find_end_block = self
            .context
            .append_basic_block(current_func, "trim_end_find_end");
        let alloc_block = self
            .context
            .append_basic_block(current_func, "trim_end_alloc");
        let copy_block = self
            .context
            .append_basic_block(current_func, "trim_end_copy");
        let return_block = self
            .context
            .append_basic_block(current_func, "trim_end_return");

        // Jump to empty check
        self.builder.build_unconditional_branch(empty_check_block)?;

        // Empty check: if len == 0, return empty string
        self.builder.position_at_end(empty_check_block);
        let is_empty = self.builder.build_int_compare(
            IntPredicate::EQ,
            str_len,
            i64_type.const_int(0, false),
            "is_empty",
        )?;
        self.builder
            .build_conditional_branch(is_empty, return_block, find_end_block)?;

        // Find end index (last non-whitespace character)
        self.builder.position_at_end(find_end_block);
        let end_idx_alloca = self.builder.build_alloca(i64_type, "end_idx")?;
        let end_idx =
            self.builder
                .build_int_sub(str_len, i64_type.const_int(1, false), "end_idx")?;
        self.builder.build_store(end_idx_alloca, end_idx)?;

        let end_loop = self
            .context
            .append_basic_block(current_func, "trim_end_end_loop");
        let end_loop_body = self
            .context
            .append_basic_block(current_func, "trim_end_end_loop_body");
        let end_loop_end = self
            .context
            .append_basic_block(current_func, "trim_end_end_loop_end");

        self.builder.build_unconditional_branch(end_loop)?;

        // End loop: find last non-whitespace character
        self.builder.position_at_end(end_loop);
        let end_idx_val = self
            .builder
            .build_load(i64_type, end_idx_alloca, "end_idx")?
            .into_int_value();

        // Check if end_idx >= 0
        let continue_end = self.builder.build_int_compare(
            IntPredicate::SGE,
            end_idx_val,
            i64_type.const_int(0, false),
            "continue_end",
        )?;
        self.builder
            .build_conditional_branch(continue_end, end_loop_body, end_loop_end)?;

        // End loop body: check if character is whitespace
        self.builder.position_at_end(end_loop_body);
        let end_char_ptr = unsafe {
            self.builder
                .build_gep(i8_type, original_str_ptr, &[end_idx_val], "end_char_ptr")?
        };
        let end_char = self
            .builder
            .build_load(i8_type, end_char_ptr, "end_char")?
            .into_int_value();

        // Check if character is whitespace
        let is_end_whitespace = self.builder.build_or(
            self.builder.build_or(
                self.builder.build_or(
                    self.builder.build_int_compare(
                        IntPredicate::EQ,
                        end_char,
                        i8_type.const_int(32, false), // space
                        "is_end_space",
                    )?,
                    self.builder.build_int_compare(
                        IntPredicate::EQ,
                        end_char,
                        i8_type.const_int(9, false), // tab
                        "is_end_tab",
                    )?,
                    "is_end_space_or_tab",
                )?,
                self.builder.build_int_compare(
                    IntPredicate::EQ,
                    end_char,
                    i8_type.const_int(10, false), // newline
                    "is_end_newline",
                )?,
                "is_end_space_tab_or_newline",
            )?,
            self.builder.build_int_compare(
                IntPredicate::EQ,
                end_char,
                i8_type.const_int(13, false), // carriage return
                "is_end_cr",
            )?,
            "is_end_whitespace",
        )?;

        // If not whitespace, we found the end; otherwise decrement
        let end_found_block = self
            .context
            .append_basic_block(current_func, "trim_end_end_found");
        let end_decrement_block = self
            .context
            .append_basic_block(current_func, "trim_end_end_decrement");

        self.builder.build_conditional_branch(
            is_end_whitespace,
            end_decrement_block,
            end_found_block,
        )?;

        // Decrement end index
        self.builder.position_at_end(end_decrement_block);
        let prev_end_idx = self.builder.build_int_sub(
            end_idx_val,
            i64_type.const_int(1, false),
            "prev_end_idx",
        )?;
        self.builder.build_store(end_idx_alloca, prev_end_idx)?;
        self.builder.build_unconditional_branch(end_loop)?;

        // End found: calculate length and allocate
        self.builder.position_at_end(end_found_block);
        let final_end_idx = self
            .builder
            .build_load(i64_type, end_idx_alloca, "final_end_idx")?
            .into_int_value();
        let trimmed_len = self.builder.build_int_add(
            final_end_idx,
            i64_type.const_int(1, false),
            "trimmed_len",
        )?;
        let trimmed_len_plus_one = self.builder.build_int_add(
            trimmed_len,
            i64_type.const_int(1, false),
            "trimmed_len_plus_one",
        )?;

        // Check if trimmed length is 0
        let is_trimmed_empty = self.builder.build_int_compare(
            IntPredicate::EQ,
            trimmed_len,
            i64_type.const_int(0, false),
            "is_trimmed_empty",
        )?;
        self.builder
            .build_conditional_branch(is_trimmed_empty, return_block, alloc_block)?;

        // Allocate memory for trimmed string
        self.builder.position_at_end(alloc_block);
        let malloc_fn_type = i8_ptr_type.fn_type(&[i64_type.into()], false);
        let malloc_fn = if let Some(func) = self.module.get_function("malloc") {
            func
        } else {
            self.module.add_function("malloc", malloc_fn_type, None)
        };

        let result_str = self
            .builder
            .build_call(malloc_fn, &[trimmed_len_plus_one.into()], "result_str")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_pointer_value();

        self.builder.build_unconditional_branch(copy_block)?;

        // Copy the trimmed substring
        self.builder.position_at_end(copy_block);
        let copy_idx_alloca = self.builder.build_alloca(i64_type, "copy_idx")?;
        self.builder
            .build_store(copy_idx_alloca, i64_type.const_int(0, false))?;

        let copy_loop = self
            .context
            .append_basic_block(current_func, "trim_end_copy_loop");
        let copy_loop_body = self
            .context
            .append_basic_block(current_func, "trim_end_copy_loop_body");
        let copy_loop_end = self
            .context
            .append_basic_block(current_func, "trim_end_copy_loop_end");

        self.builder.build_unconditional_branch(copy_loop)?;

        // Copy loop
        self.builder.position_at_end(copy_loop);
        let copy_idx = self
            .builder
            .build_load(i64_type, copy_idx_alloca, "copy_idx")?
            .into_int_value();

        // Check if copy_idx < trimmed_len
        let continue_copy = self.builder.build_int_compare(
            IntPredicate::SLT,
            copy_idx,
            trimmed_len,
            "continue_copy",
        )?;
        self.builder
            .build_conditional_branch(continue_copy, copy_loop_body, copy_loop_end)?;

        // Copy loop body
        self.builder.position_at_end(copy_loop_body);
        let src_char_ptr = unsafe {
            self.builder
                .build_gep(i8_type, original_str_ptr, &[copy_idx], "src_char_ptr")?
        };
        let src_char = self
            .builder
            .build_load(i8_type, src_char_ptr, "src_char")?
            .into_int_value();

        let dst_char_ptr = unsafe {
            self.builder
                .build_gep(i8_type, result_str, &[copy_idx], "dst_char_ptr")?
        };
        self.builder.build_store(dst_char_ptr, src_char)?;

        // Increment copy index
        let next_copy_idx =
            self.builder
                .build_int_add(copy_idx, i64_type.const_int(1, false), "next_copy_idx")?;
        self.builder.build_store(copy_idx_alloca, next_copy_idx)?;
        self.builder.build_unconditional_branch(copy_loop)?;

        // Copy loop end: add null terminator
        self.builder.position_at_end(copy_loop_end);
        let null_ptr = unsafe {
            self.builder
                .build_gep(i8_type, result_str, &[trimmed_len], "null_ptr")?
        };
        self.builder
            .build_store(null_ptr, i8_type.const_int(0, false))?;
        self.builder.build_unconditional_branch(return_block)?;

        // End loop end: all characters are whitespace
        self.builder.position_at_end(end_loop_end);
        self.builder.build_unconditional_branch(return_block)?;

        // Return block with PHI node
        self.builder.position_at_end(return_block);
        let result_phi = self.builder.build_phi(i8_ptr_type, "trim_end_result")?;

        // Empty string case: return ""
        let empty_str = self.builder.build_global_string_ptr("", "empty_str")?;
        result_phi.add_incoming(&[(&empty_str, empty_check_block)]);
        result_phi.add_incoming(&[(&empty_str, end_loop_end)]);
        result_phi.add_incoming(&[(&empty_str, end_found_block)]);

        // Normal case: return trimmed string
        result_phi.add_incoming(&[(&result_str, copy_loop_end)]);

        Ok(result_phi.as_basic_value())
    }
}
